#include <string>
#include <vector>
#include "gtest/gtest.h"
#include "Utils/ObjectPool.h"

using namespace std;
using namespace RayFrame;

class TestClass
{
protected:
  int m_new, m_del;
public:
  virtual void internalCreate()
  {
    int static data = 0;
    m_new = data++;
  }
  virtual void internalDestroy()
  {
    int static data = 0;
    m_del = data++;
  }
public:
  TestClass()
  {
    m_new = m_del = 0;
  }

  int CreatedTimes()
  {
    return m_new;
  }

  int DestroiedTimes()
  {
    return m_del;
  }

  bool operator == (TestClass& rhs)
  {
    return m_del == rhs.m_del && m_new == rhs.m_new;
  }
};

TEST(ObjectPool, Create) 
{
  const int pool_size = 64;
  Pool<TestClass> pool;
  
  pool.Init(-123);

  EXPECT_EQ(pool.GetFreeCount(), 0);
  EXPECT_EQ(pool.GetUsedCount(), 0);
  EXPECT_EQ(pool.GetCapability(), 0);
  
}

typedef Pool<TestClass> TestPool;
typedef Pool<TestClass>::iterator TestIter;

TEST(ObjectPool, PopPush)
{
  const int pool_size = 5;
  Pool<TestClass> pool;
  vector<Pool<TestClass>::iterator> vecPool;

  pool.Init(pool_size);
  
  for(int i=0; i<pool_size; i++)
  {
    Pool<TestClass>::iterator item = pool.Pop();
    EXPECT_TRUE(item != pool.End());
    EXPECT_EQ(pool.GetFreeCount(), pool_size-i-1);
    EXPECT_EQ(pool.GetUsedCount(), i+1);

    EXPECT_EQ(item.GetData()->CreatedTimes(), i);

    vecPool.push_back(item);
  }
  
  int i = 0;
  for(vector<Pool<TestClass>::iterator>::iterator iter = vecPool.begin();
    iter != vecPool.end(); iter++)
  {
    i++;
    pool.Push(*iter);
    EXPECT_EQ(pool.GetFreeCount(), i);
    EXPECT_EQ(pool.GetUsedCount(), pool_size - i);
  }

  TestIter iter1 = pool.Pop();
  TestIter iter2 = pool.Pop();
  TestIter iter3 = pool.Pop();
  TestIter iter4 = pool.Pop();

  pool.Push(iter2);
  pool.Push(iter4);
  pool.Push(iter1);
  pool.Push(iter3);

  EXPECT_EQ(pool.GetFreeCount(), pool_size);
  int count = 0;
  for(TestIter iter = pool.FreeBegin(); iter != pool.End(); iter++)
  {
    count++;
  }
  EXPECT_EQ(count, pool_size);
}

TEST(ObjectPool, Tranvse)
{
  const int pool_size = 5;
  typedef Pool<TestClass>::iterator TestIter;
  Pool<TestClass> pool;
  pool.Init(pool_size);

  // make sure poped item doesn't appear in free list
  while(true)
  {
    TestIter iter = pool.Pop();
    if(iter == pool.End())
      break;

    for(TestIter iter2 = pool.FreeBegin(); iter2 != pool.End(); iter2++ )
    {
      if(iter == iter2)
        int debug = 1;
      EXPECT_TRUE(iter != iter2);
    }
  }
  
}
